package com.guodi.model.utils;

import com.aspose.words.*;
import com.aspose.words.Shape;
import com.aspose.words.net.System.Data.DataRow;
import com.aspose.words.net.System.Data.DataTable;
import org.apache.commons.lang.StringUtils;

import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @描述：Word工具类
 * @作者：戴伙进
 * @日期：2020/12/5 16:07
 **/
public class NewWordUtil {

    static {
        getLicense();
    }

    /**
     * 获取注册文件
     */
    public static void getLicense() {
        InputStream is;
        try {
            is = com.guodi.model.utils.NewWordUtil.class.getClassLoader().getResourceAsStream("license.xml");
            License license = new License();
            license.setLicense(is);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 打印word或pdf
     *
     * @param model           模板路径
     * @param fieldData       域字段数据
     * @param tableData       子表数据
     * @param imageData       图片数据
     * @param fileType        保存文件类型
     * @param wmText          水印文字
     * @param saveRoot        保存跟路径
     * @param mergerTableList 合并表格列表
     * @return
     * @throws Exception
     */
    public static String wordprint(InputStream model, List<Map<String, Object>> fieldData, List<Map<String, Object>> tableData, List<Map<String, Object>> imageData, String fileType, String wmText, String saveRoot, List<Map<String, Object>> mergerTableList) throws Exception {
        String result = null;
        Document document = new Document(model);
        DocumentBuilder builder = new DocumentBuilder(document);
        /*域字段处理*/
        if (fieldData != null && fieldData.size() > 0) {
            List<String> keyList = new LinkedList<String>();
            List<Object> values = new LinkedList<Object>();
            for (int i = 0; i < fieldData.size(); i++) {
                List<Map<String, Object>> fieids;
                //支持两种格式，那就兼容一下吧
                if (fieldData.get(i).get("table") instanceof LinkedHashMap || fieldData.get(i).get("table") instanceof HashMap) {
                    Map<String, Object> map = (Map<String, Object>) fieldData.get(i).get("table");
                    fieids = (List<Map<String, Object>>) map.get("datas");
                } else {
                    fieids = (List<Map<String, Object>>) fieldData.get(i).get("table");
                }
                if (fieids != null) {
                    for (int j = 0; j < fieids.size(); j++) {
                        Iterator iterator = fieids.get(j).entrySet().iterator();
                        while (iterator.hasNext()) {
                            Map.Entry entry = (Map.Entry) iterator.next();
                            //插入html
                            if (fieldData.get(i).get("type") != null && "html".equalsIgnoreCase(fieldData.get(i).get("type").toString())) {
                                builder.moveToMergeField(entry.getKey().toString());
                                builder.insertHtml(entry.getValue() != null ? entry.getValue().toString() : " ");
                            } else {
                                //普通文本域
                                keyList.add(entry.getKey().toString());
                                values.add(entry.getValue());
                            }

                        }
                    }
                }
            }
            String[] keys = new String[keyList.size()];
            for (int i = 0; i < keyList.size(); i++) {
                keys[i] = keyList.get(i).toString();
            }
            document.getMailMerge().execute(keys, values.toArray());
        }
        Map<String, Object> photoMap = new HashMap<String, Object>();
        /*列表数据处理*/
        if (tableData != null && tableData.size() > 0) {
            for (int i = 0; i < tableData.size(); i++) {
                String tableName = tableData.get(i).get("name").toString();
                DataTable dataTable = new DataTable(tableName);//表格名称
                List<Map<String, Object>> list = (List<Map<String, Object>>) tableData.get(i).get("table");
                if (list != null && list.size() > 0) {
                    //字段
                    Set<String> columns = list.get(0).keySet();
                    for (String column : columns) {
                        dataTable.getColumns().add(column);
                    }
                    //数据填充
                    for (int j = 0; j < list.size(); j++) {
                        DataRow row = dataTable.newRow(); //新增一行
                        int index = 0;
                        Iterator iterator = list.get(j).entrySet().iterator();
                        while (iterator.hasNext()) {
                            Map.Entry entry = (Map.Entry) iterator.next();
                            String value = entry.getValue() == null ? "" : entry.getValue().toString();
                            if (value.toString().startsWith("imageSource@")) {
                                photoMap.put("#@" + tableName + j + "@#", value);
                                value = "#@" + tableName + j + "@#";
                            } else if (value.toString().startsWith("QRCode@")) {
                                photoMap.put("#@" + tableName + j + "@#", value);
                                value = "#@" + tableName + j + "@#";
                            }
                            row.set(index, value);
                            index++;
                        }
                        dataTable.getRows().add(row);//加入此行数据
                    }
                }

                //对于无数据的情况，增加一行空记录
                if (dataTable.getRows().getCount() == 0) {
                    DataRow row = dataTable.newRow();
                    dataTable.getRows().add(row);
                }
                document.getMailMerge().setFieldMergingCallback(new com.guodi.model.utils.HandleMergeFieldInsertDocument(com.guodi.model.utils.ConvertUtil.tryObjToDouble(tableData.get(i).get("width"), 128), com.guodi.model.utils.ConvertUtil.tryObjToDouble(tableData.get(i).get("height"), 128)));
                document.getMailMerge().executeWithRegions(dataTable); //调用接口
            }
        }

        //处理子表图片
        if (!photoMap.isEmpty()) {
            Iterator iterator = photoMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator.next();
                String value = entry.getValue().toString();
                String key = entry.getKey().toString();
                Pattern reg = Pattern.compile(key);
                String imageUrl = null;
                if (value.toString().startsWith("imageSource@")) {
                    imageUrl = value.substring(value.lastIndexOf("@") + 1, value.length());
                }
                String qrcodeContent = null;
                if (value.toString().startsWith("QRCode@")) {
                    qrcodeContent = value.substring(value.lastIndexOf("@") + 1, value.length());
                }
                double width = 64;
                double height = 64;
                value = value.substring(0, value.lastIndexOf("@"));
                if (value.indexOf("@") != value.lastIndexOf("@")) {
                    width = com.guodi.model.utils.ConvertUtil.tryStrToDouble(value.substring(value.indexOf("@") + 1, value.lastIndexOf("@")), width);
                }
                if (value.length() != value.lastIndexOf("@")) {
                    height = com.guodi.model.utils.ConvertUtil.tryStrToDouble(value.substring(value.lastIndexOf("@") + 1, value.length()), height);
                }

//                document.getRange().replace(reg, new com.guodi.model.utils.ReplaceImage(imageUrl, qrcodeContent, width, height, "grid"), false);
                document.getRange().replace(reg, "");
            }
        }
        /*图片数据处理*/
        if (imageData != null && imageData.size() > 0) {
            for (int i = 0; i < imageData.size(); i++) {
                String path = imageData.get(i).get("imageSource").toString();
                String imageType = imageData.get(i).get("imageType").toString();
                String imageName = imageData.get(i).get("imageName").toString();
                double width = com.guodi.model.utils.ConvertUtil.tryObjToDouble(imageData.get(i).get("width"), 0);
                double height = com.guodi.model.utils.ConvertUtil.tryObjToDouble(imageData.get(i).get("height"), 0);
                boolean flag = false;
                //非@#imageName#@模式
                if (!(imageName.startsWith("@#") && imageName.endsWith("#@"))) {
                    //域模式
                    flag = builder.moveToMergeField(imageName);
                    //书签模式
                    if (flag == false) {
                        builder.moveToBookmark(imageName);
                    }
                }
                //imageType==1是二维码
                if (imageType != null && "1".equals(imageType)) {
                    if (path != null) {
                        if (flag == true) {
                            builder.insertImage(com.guodi.model.utils.QRCodeUtil.createQrCode(path, 600, 600), width, height);
                        } else {
                            Pattern reg = Pattern.compile(imageName);
//                            document.getRange().replace(reg, new com.guodi.model.utils.ReplaceImage(null, path, width, height, null), false);
                            document.getRange().replace(reg, "");
                        }
                    }
                } else {
                    File file = new File(path);
                    if (file.exists()) {
                        if (flag == true) {
                            builder.insertImage(path, width, height);
                        } else {
                            Pattern reg = Pattern.compile(imageName);
                            //todo 临时解决
//                            document.getRange().replace(reg, new com.guodi.model.utils.ReplaceImage(path, null, width, height, null), false);
                            document.getRange().replace(reg, "");
                        }
                    }
                }
            }
        }
        //水印
        if (!StringUtils.isEmpty(wmText)) {
            waterMark(document, wmText);
        }

        //进行表格单元格合并
        if (mergerTableList != null && mergerTableList.size() > 0) {
            for (int i = 0; i < mergerTableList.size(); i++) {
                Map map = mergerTableList.get(i);
                //表格序号
                Integer xh = (Integer) map.get("xh");
                Table table = (Table) document.getChild(NodeType.TABLE, xh, true); //哪几个表格
                List<Map<String, Object>> rowCells = (List<Map<String, Object>>) map.get("cells");
                for (int j = 0; j < rowCells.size(); j++) {
                    Map<String, Object> rowCell = rowCells.get(j);
                    String rowCellStr = (String) rowCell.get("rowCell");
                    String[] rowCellAry = rowCellStr.split(",");
                    Integer startRow = Integer.parseInt(rowCellAry[0]);
                    Integer startCell = Integer.parseInt(rowCellAry[1]);
                    Integer endRow = Integer.parseInt(rowCellAry[2]);
                    Integer endCell = Integer.parseInt(rowCellAry[3]);
                    Cell cellStartRange = table.getRows().get(startRow).getCells().get(startCell); //第2行第1列
                    Cell cellEndRange = table.getRows().get(endRow).getCells().get(endCell); //第3行第1列
                    // Merge all the cells between the two specified cells into one.
                    mergeCells(cellStartRange, cellEndRange);
                }
            }
        }
        if (fileType.indexOf(",") > 0) {
            result = "";
            String[] types = fileType.split(",");
            for (int i = 0; i < types.length; i++) {
                String path = FileUtil.tempFilePath + UUID.randomUUID().toString() + "." + types[i];
                result += path + ",";
                document.save(saveRoot + path);
            }
            result = result.substring(0, result.length() - 1);
        } else {
            result = FileUtil.tempFilePath + UUID.randomUUID().toString() + "." + fileType;
            document.save(saveRoot + result);
        }
        return result;
    }


    /**
     * 加水印
     *
     * @param mdoc
     * @param wmText
     * @throws Exception
     */
    private static void waterMark(Document mdoc, String wmText) throws Exception {
        Shape waterShape = new Shape(mdoc, ShapeType.TEXT_PLAIN_TEXT);
        //设置该文本的水印
        waterShape.getTextPath().setText(wmText);
        waterShape.getTextPath().setFontFamily("Arial");
        waterShape.setWidth(500);
        waterShape.setHeight(100);
        //文本将从左下角到右上角。
        waterShape.setRotation(-40);
        //绘制水印颜色
        waterShape.setFillColor(Color.LIGHT_GRAY);//浅灰色水印
        waterShape.setStrokeColor(Color.LIGHT_GRAY);
        //将水印放置在页面中心
        waterShape.setRelativeHorizontalPosition(RelativeHorizontalPosition.PAGE);
        waterShape.setRelativeVerticalPosition(RelativeVerticalPosition.PAGE);
        waterShape.setWrapType(WrapType.NONE);
        waterShape.setVerticalAlignment(VerticalAlignment.CENTER);
        waterShape.setHorizontalAlignment(HorizontalAlignment.CENTER);

        // 创建一个新段落并在该段中添加水印。
        Paragraph watermarkPara = new Paragraph(mdoc);

        watermarkPara.appendChild(waterShape);

        // 在每个部分中，最多可以有三个不同的标题，因为我们想要出现在所有页面上的水印，插入到所有标题中。
        for (Section sect : mdoc.getSections()) {
            // 每个区段可能有多达三个不同的标题，因为我们希望所有页面上都有水印，将所有的头插入。
            InsertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_PRIMARY);
            InsertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_FIRST);
            InsertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_EVEN);
        }
    }

    /**
     * word合并
     *
     * @param list
     * @throws Exception
     */
    public static String wordCombine(List<String> list, String fileType, String saveRoot, boolean sfEnd) throws Exception {
        if (list == null || list.size() < 2) {
            throw new Exception("参数不对!");
        }
        String path = list.get(0);
        File file = new File(path);
        if (!file.exists()) {
            path = saveRoot + path;
        }
        if (!file.exists()) {
            throw new Exception("找不到文件" + list.get(0) + "!");
        }

        Document documentAll = new Document(path);
        if (sfEnd) {
            //这里添加时反转添加的，故需要倒序添加
            for (int i = list.size() - 1; i > 0; i--) {
                path = list.get(i);
                if (!file.exists()) {
                    path = saveRoot + path;
                }
                if (!file.exists()) {
                    throw new Exception("找不到文件" + list.get(i) + "!");
                }
                Document document = new Document(path);
                //是否是文档内容最末尾上拼接
                appendDoc(documentAll, document, false);

            }
        } else {
            for (int i = 1; i < list.size(); i++) {
                path = list.get(i);
                if (!file.exists()) {
                    path = saveRoot + path;
                }
                if (!file.exists()) {
                    throw new Exception("找不到文件" + list.get(i) + "!");
                }
                Document document = new Document(path);
                documentAll.appendDocument(document, ImportFormatMode.USE_DESTINATION_STYLES);
            }
        }
        fileType = (fileType == null || fileType.length() == 0) ? "docx" : fileType;
        String fileName = UUID.randomUUID().toString() + "." + fileType;
        String result = FileUtil.tempFilePath + fileName;
        documentAll.save(saveRoot + result);
        return fileName;
    }

    private static void InsertWatermarkIntoHeader(Paragraph watermarkPara, Section sect, int headerType) throws Exception {
        HeaderFooter header = sect.getHeadersFooters().get(headerType);
        if (header == null) {
            // 当前节中没有指定类型的头，创建它
            header = new HeaderFooter(sect.getDocument(), headerType);
            sect.getHeadersFooters().add(header);
        }
        // 在头部插入一个水印的克隆
        header.appendChild(watermarkPara.deepClone(true));
    }

    /**
     * word另存为
     *
     * @param filepath
     * @param fileType
     * @param saveRoot
     * @return
     * @throws Exception
     */
    public static String wordsaveAs(String filepath, String fileType, String saveRoot) throws Exception {
        String path = filepath;
        if (!FileUtil.exists(path)) {
            path = saveRoot + path;
        }
        if (!FileUtil.exists(path)) {
            throw new Exception("找不到文件" + filepath + "!");
        }
        Document document = new Document(path);
        String result = FileUtil.tempFilePath + UUID.randomUUID().toString() + "." + fileType;
        document.save(saveRoot + result);
        return result;
    }


    /**
     * @param mainDoc      主文档
     * @param tobeInserted 拼接的文档
     * @param bookmark     书签
     * @Description 向书签后插入文档
     * @Return com.aspose.words.Document
     * @Author Mr.Walloce
     * @Date 2019/7/27 18:33
     */
    private static Document insertDocumentAfterBookMark(Document mainDoc, Document tobeInserted, String bookmark)
            throws Exception {
        if (mainDoc == null) {
            return null;
        } else if (tobeInserted == null) {
            return mainDoc;
        } else {
            //构建新文档
            DocumentBuilder mainDocBuilder = new DocumentBuilder(mainDoc);
            if (bookmark != null && bookmark.length() > 0) {
                //获取到书签
                BookmarkCollection bms = mainDoc.getRange().getBookmarks();
                Bookmark bm = bms.get(bookmark);
                if (bm != null) {
                    mainDocBuilder.moveToBookmark(bookmark, true, false);
                    mainDocBuilder.writeln();
                    //获取到插入的位置
                    Node insertAfterNode = mainDocBuilder.getCurrentParagraph().getPreviousSibling();
                    insertDocumentAfterNode(insertAfterNode, mainDoc, tobeInserted);
                }
            } else {
                appendDoc(mainDoc, tobeInserted, true);
            }

            return mainDoc;
        }
    }

    /**
     * @param insertAfterNode 插入的位置
     * @param mainDoc
     * @param srcDoc
     * @Description
     * @Return void
     * @Author Mr.Walloce
     * @Date 2019/7/27 14:51
     */
    private static void insertDocumentAfterNode(Node insertAfterNode, Document mainDoc, Document srcDoc)
            throws Exception {
        if (insertAfterNode.getNodeType() != 8 & insertAfterNode.getNodeType() != 5) {
            throw new Exception("The destination node should be either a paragraph or table.");
        } else {
            CompositeNode dstStory = insertAfterNode.getParentNode();

            while (null != srcDoc.getLastSection().getBody().getLastParagraph()
                    && !srcDoc.getLastSection().getBody().getLastParagraph().hasChildNodes()) {
                srcDoc.getLastSection().getBody().getLastParagraph().remove();
            }

            NodeImporter importer = new NodeImporter(srcDoc, mainDoc, 1);
            int sectCount = srcDoc.getSections().getCount();

            for (int sectIndex = 0; sectIndex < sectCount; ++sectIndex) {
                Section srcSection = srcDoc.getSections().get(sectIndex);
                int nodeCount = srcSection.getBody().getChildNodes().getCount();

                for (int nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
                    Node srcNode = srcSection.getBody().getChildNodes().get(nodeIndex);
                    Node newNode = importer.importNode(srcNode, true);
                    dstStory.insertAfter(newNode, insertAfterNode);
                    insertAfterNode = newNode;
                }
            }

        }
    }

    /**
     * @param dstDoc
     * @param srcDoc
     * @param includeSection
     * @Description 文档拼接
     * @Return void
     * @Author Mr.Walloce
     * @Date 2019/7/27 14:53
     */
    private static void appendDoc(Document dstDoc, Document srcDoc, boolean includeSection) throws Exception {
        if (includeSection) {
            Iterator<Section> var3 = srcDoc.getSections().iterator();
            while (var3.hasNext()) {
                Section srcSection = (Section) var3.next();
                Node dstNode = dstDoc.importNode(srcSection, true, 0);
                dstDoc.appendChild(dstNode);
            }
        } else {
            Node node = dstDoc.getLastSection().getBody().getLastParagraph();
            if (node == null) {
                node = new Paragraph(srcDoc);
                dstDoc.getLastSection().getBody().appendChild(node);
            }

            if (node.getNodeType() != 8 & node.getNodeType() != 5) {
                throw new Exception("Use appendDoc(dstDoc, srcDoc, true) instead of appendDoc(dstDoc, srcDoc, false)");
            }

            insertDocumentAfterNode(node, dstDoc, srcDoc);
        }
    }

    /**
     * Merges the range of cells found between the two specified cells both
     * horizontally and vertically. Can span over multiple rows.
     *
     * @param startCell
     * @param endCell
     */
    public static void mergeCells(Cell startCell, Cell endCell) {
        Table parentTable = startCell.getParentRow().getParentTable();

// Find the row and cell indices for the start and end cell.
        Point startCellPos = new Point(startCell.getParentRow().indexOf(startCell), parentTable.indexOf(startCell.getParentRow()));
        Point endCellPos = new Point(endCell.getParentRow().indexOf(endCell), parentTable.indexOf(endCell.getParentRow()));
// Create the range of cells to be merged based off these indices. Inverse each index if the end cell if before the start cell.
        Rectangle mergeRange = new Rectangle(Math.min(startCellPos.x, endCellPos.x), Math.min(startCellPos.y, endCellPos.y), Math.abs(endCellPos.x - startCellPos.x) + 1,
                Math.abs(endCellPos.y - startCellPos.y) + 1);

        for (Row row : parentTable.getRows()) {
            for (Cell cell : row.getCells()) {
                Point currentPos = new Point(row.indexOf(cell), parentTable.indexOf(row));

// Check if the current cell is inside our merge range then merge it.
                if (mergeRange.contains(currentPos)) {
                    if (currentPos.x == mergeRange.x) {
                        cell.getCellFormat().setHorizontalMerge(CellMerge.FIRST);
                    } else {
                        cell.getCellFormat().setHorizontalMerge(CellMerge.PREVIOUS);
                    }

                    if (currentPos.y == mergeRange.y) {
                        cell.getCellFormat().setVerticalMerge(CellMerge.FIRST);
                    } else {
                        cell.getCellFormat().setVerticalMerge(CellMerge.PREVIOUS);
                    }
                }
            }
        }
    }

    /**
     * @描述： word转pdf
     * @作者：彭辉
     * @日期：2020/12/5 18:29
     **/
    public static void doc2pdf(File docFile, String pdfFilePath) {
        FileOutputStream os = null;
        FileInputStream is = null;
        try {
            System.out.println("开始转换......");
            // 临时文件，可以放在项目目录下，用完删除，但一定要有文件夹，没有的话需要创建
            File changedFile = new File(pdfFilePath);
            os = new FileOutputStream(changedFile);
            is = new FileInputStream(docFile);
            Document document = new Document(is);
            // 支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF...
            // 保存具有新名称和文件格式的文档
            document.save(os, SaveFormat.PDF);
            System.out.println("开始完成......");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    // 关闭输出流删除临时文件
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    //关闭输入流
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
